<template>
  <div class="chat-container">
    <!-- 左侧对话列表 -->
    <div class="sidebar" :class="{ 'sidebar-collapsed': sidebarCollapsed }">
      <div class="sidebar-header">
        <div class="header-content">
          <h2>AI对话</h2>
          <button @click="toggleSidebar" class="sidebar-toggle">
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
            >
              <line x1="3" y1="6" x2="21" y2="6"></line>
              <line x1="3" y1="12" x2="21" y2="12"></line>
              <line x1="3" y1="18" x2="21" y2="18"></line>
            </svg>
          </button>
        </div>
        <button
          @click="addNewConversation"
          class="btn btn-primary new-chat-btn"
          :title="sidebarCollapsed ? '新建对话' : ''"
        >
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
          >
            <line x1="12" y1="5" x2="12" y2="19"></line>
            <line x1="5" y1="12" x2="19" y2="12"></line>
          </svg>
          <span v-if="!sidebarCollapsed">新对话</span>
        </button>
      </div>

      <div
        class="conversation-list"
        ref="conversationListRef"
        @scroll="handleConversationListScroll"
      >
        <!-- 无对话时的缺省状态 -->
        <el-empty
          v-if="conversations.length === 0 && !loading"
          description="暂无对话记录"
          :image-size="80"
        />

        <div
          v-for="conversation in conversations"
          :key="conversation.id"
          :data-id="conversation.id"
          :class="[
            'conversation-item',
            {
              active: conversation.id === currentConversationId,
              switching:
                isSwitchingConversation &&
                conversation.id === currentConversationId,
            },
          ]"
          @click="
            !editingConversationId && handleSwitchConversation(conversation.id)
          "
        >
          <div class="conversation-content">
            <div
              v-if="editingConversationId !== conversation.id"
              class="conversation-title"
            >
              {{ conversation.title }}
            </div>
            <el-input
              v-else
              v-model="editingTitle"
              @blur="handleSaveTitle(conversation.id)"
              @keyup.enter="handleSaveTitle(conversation.id)"
              @keyup.esc="handleCancelEdit"
              class="title-input"
              size="small"
              maxlength="50"
              show-word-limit
            />
            <div
              class="conversation-preview"
              v-if="
                conversation.messages &&
                conversation.messages.length > 0 &&
                editingConversationId !== conversation.id
              "
            >
              {{ getLastMessage(conversation) }}
            </div>
            <div
              class="conversation-time"
              v-if="editingConversationId !== conversation.id"
            >
              {{ formatTime(conversation.updatedAt) }}
            </div>
          </div>
          <div class="conversation-actions">
            <button
              v-if="!conversation.isFront || hasValidMessages(conversation)"
              @click.stop="handleSettings(conversation.id)"
              class="settings-btn"
              title="设置"
            >
              <el-icon><Setting /></el-icon>
            </button>
            <button
              v-if="
                (!conversation.isFront || hasValidMessages(conversation)) &&
                !(
                  conversations.length === 1 &&
                  conversation.isFront &&
                  !hasValidMessages(conversation)
                )
              "
              @click.stop="handleDeleteConversation(conversation.id)"
              class="delete-btn"
              title="删除对话"
            >
              <el-icon><Delete /></el-icon>
            </button>
          </div>
        </div>

        <!-- 加载更多提示 -->
        <div v-if="loadingMore" class="loading-more">
          <el-icon class="is-loading"><Loading /></el-icon>
          <span>加载中...</span>
        </div>
        <div
          v-else-if="!pagination.hasNext && conversations.length > 0"
          class="no-more"
        >
          没有更多了
        </div>

        <!-- 收起状态下的提示 -->
        <div
          v-if="sidebarCollapsed"
          class="collapsed-hint"
          @click="toggleSidebar"
        >
          <div class="hint-icon">💬</div>
          <div class="hint-text">点击展开对话列表</div>
        </div>
      </div>
    </div>

    <!-- 右侧对话主体 -->
    <div class="main-content">
      <!-- 页面标题 -->
      <div class="page-title-bar">
        <div class="title-content">
          <img src="@/assets/image/logo.png" alt="Logo" class="logo-icon" />
          <h1 class="main-title">南方电网广东佛山南海供电公司</h1>
        </div>
        <button
          @click="toggleSidebar"
          class="mobile-sidebar-toggle"
          v-if="isMobile"
        >
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
          >
            <line x1="3" y1="6" x2="21" y2="6"></line>
            <line x1="3" y1="12" x2="21" y2="12"></line>
            <line x1="3" y1="18" x2="21" y2="18"></line>
          </svg>
        </button>
      </div>

      <div class="chat-header">
        <div class="header-left">
          <h3>{{ currentConversation?.title || "新对话" }}</h3>
        </div>
        <div class="header-actions">
          <!-- <el-dropdown
            @command="handleThemeChange"
            trigger="click"
            placement="bottom-end"
          >
            <button
              class="theme-btn-header"
              title="主题设置"
            >
              <el-icon><Setting /></el-icon>
              <span class="btn-text">主题</span>
            </button>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item
                  v-for="theme in availableThemes"
                  :key="theme.key"
                  :command="theme.key"
                  :class="{ active: currentTheme === theme.key }"
                >
                  <span>{{ theme.name }}</span>
                  <el-icon v-if="currentTheme === theme.key" style="margin-left: 8px;">
                    <Check />
                  </el-icon>
                </el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown> -->
          <button
            @click="goToTestMdPreview"
            class="test-page-btn"
            title="预览测试数据"
          >
            <el-icon><Tickets /></el-icon>
            <span class="btn-text">测试数据</span>
          </button>
          <button
            @click="goToMarkdownPlayground"
            class="test-page-btn alt"
            title="Markdown 实验"
          >
            <el-icon><Collection /></el-icon>
            <span class="btn-text">Markdown 实验</span>
          </button>
          <button
            @click="goToKnowledgeBase"
            class="knowledge-base-btn-header"
            title="知识库管理"
          >
            <el-icon><Document /></el-icon>
            <span class="btn-text">知识库</span>
          </button>
          <button
            @click="handleLogout"
            class="logout-btn-header"
            title="退出登录"
          >
            <el-icon><User /></el-icon>
            <span class="btn-text">退出</span>
          </button>
          <button
            @click="handleClearConversation"
            class="clear-btn"
            title="清空对话"
            :disabled="!currentConversation?.messages.length"
          >
            <el-icon><Delete /></el-icon>
            <span class="btn-text">清空</span>
          </button>
        </div>
      </div>

      <div class="messages-container" ref="messagesContainer">
        <div
          v-if="currentConversation?.messages.length === 0"
          class="empty-state"
        >
          <div class="empty-icon">
            <div class="slogan-text">人民电业为人民</div>
          </div>
          <h3>开始新的对话</h3>
          <p>输入消息开始与AI对话，支持文件上传</p>
        </div>

        <div v-else class="messages">
          <div
            v-for="message in currentConversation?.messages"
            :key="message.id"
            :class="['message', message.role]"
          >
            <div class="message-avatar">
              <div class="avatar-icon" :class="message.role">
                <svg
                  v-if="message.role === 'user'"
                  class="user-icon"
                  viewBox="0 0 24 24"
                  aria-hidden="true"
                >
                  <circle cx="12" cy="8" r="4" fill="#fff" />
                  <path d="M4 20c0-4 4-6 8-6s8 2 8 6" fill="#fff" />
                </svg>
                <img
                  v-else
                  src="@/assets/image/logo.png"
                  alt="AI"
                  class="ai-avatar-img"
                />
              </div>
            </div>
            <div class="message-content">
              <!-- 文件显示在对话框上方 -->
              <div
                v-if="message.files && message.files.length > 0"
                class="message-files"
              >
                <div
                  v-for="file in message.files"
                  :key="file.id || file.name"
                  class="file-item"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                  >
                    <path
                      d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2Z"
                    ></path>
                    <polyline points="14,2 14,8 20,8"></polyline>
                  </svg>
                  <span class="file-name-text">{{
                    file.name || file.filename || "未知文件"
                  }}</span>
                  <span class="file-size" v-if="file.size"
                    >({{ formatFileSize(file.size) }})</span
                  >
                </div>
              </div>
              <!-- 如果只有文件没有文本内容，显示时间在文件下方 -->
              <div
                v-if="
                  !message.content && message.files && message.files.length > 0
                "
                class="message-time-only"
              >
                {{ formatTime(message.timestamp) }}
              </div>
              <!-- 消息对话框 -->
              <div
                v-if="
                  message.content ||
                  !message.files ||
                  message.files.length === 0
                "
                class="message-bubble"
                :class="message.role"
              >
                <!-- AI消息的复制按钮 -->
                <button
                  v-if="
                    (message.role === 'assistant' || message.role === 'ai') &&
                    message.content &&
                    message.content.trim() &&
                    !message.isWaiting
                  "
                  @click="copyMessageContent(message.content, message.id)"
                  class="copy-button"
                  :title="
                    copiedMessageId === message.id ? '已复制' : '复制内容'
                  "
                >
                  <el-icon> <CopyDocument /> </el-icon>
                  <span v-if="copiedMessageId === message.id" class="copy-tip"
                    >已复制</span
                  >
                </button>
                <!-- 消息文本内容 -->
                <div
                  v-if="message.content && message.content.trim()"
                  class="message-text"
                  v-html="formatMessage(message.content)"
                ></div>
                <!-- 空内容友好提示 -->
                <div
                  v-else-if="!message.content || !message.content.trim()"
                  class="message-empty-hint"
                >
                  <div class="empty-hint-content">
                    <span class="empty-hint-icon">
                      {{
                        message.role === "ai"
                          ? message.isWaiting
                            ? "⏳"
                            : "😔"
                          : "💬"
                      }}
                    </span>
                    <span class="empty-hint-text">
                      {{
                        message.role === "ai"
                          ? message.isWaiting
                            ? "正在获取信息中，请您耐心等待..."
                            : "抱歉，未能获取到回答内容。可能是网络问题或服务暂时不可用，请稍后重试。"
                          : "消息内容为空"
                      }}
                    </span>
                  </div>
                </div>
                <div class="message-time">
                  {{ formatTime(message.timestamp) }}
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <div class="input-container">
        <!-- 已上传文件列表 -->
        <div
          v-if="selectedFiles.length > 0 || uploadingFiles.size > 0"
          class="selected-files"
        >
          <div
            class="file-limit-tip"
            v-if="selectedFiles.length > 0 || uploadingFiles.size > 0"
          >
            已上传 {{ selectedFiles.length }}/5 个文件
          </div>
          <div class="file-list-container">
            <!-- 正在上传的文件 -->
            <div
              v-for="fileName in uploadingFiles"
              :key="`uploading-${fileName}`"
              class="file-preview uploading"
            >
              <div class="file-info">
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="2"
                >
                  <path
                    d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2Z"
                  ></path>
                  <polyline points="14,2 14,8 20,8"></polyline>
                </svg>
                <span class="file-name">{{ fileName }}</span>
                <span class="file-status">上传中...</span>
              </div>
            </div>
            <!-- 已上传的文件 -->
            <div
              v-for="(file, index) in selectedFiles"
              :key="file.id || index"
              class="file-preview"
            >
              <div class="file-info">
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="2"
                >
                  <path
                    d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2Z"
                  ></path>
                  <polyline points="14,2 14,8 20,8"></polyline>
                </svg>
                <span class="file-name">{{ file.filename || file.name }}</span>
                <span class="file-size" v-if="file.size"
                  >({{ formatFileSize(file.size) }})</span
                >
              </div>
              <button
                @click="removeFile(index)"
                class="remove-file-btn"
                title="移除文件"
              >
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="2"
                >
                  <line x1="18" y1="6" x2="6" y2="18"></line>
                  <line x1="6" y1="6" x2="18" y2="18"></line>
                </svg>
              </button>
            </div>
          </div>
        </div>

        <div class="input-wrapper">
          <div
            class="file-upload"
            :class="{ disabled: !currentConversationId }"
            @click="triggerFileInput"
            :title="currentConversationId ? '上传文件' : '请先选择或创建对话'"
          >
            <input
              type="file"
              ref="fileInput"
              @change="handleFileSelect"
              @click.stop
              multiple
              accept=".doc,.docx,.xls,.xlsx,.pdf,.ppt,.pptx,.txt"
              :disabled="!currentConversationId"
              style="display: none"
            />
            <el-icon class="upload-icon"><Upload /></el-icon>
          </div>

          <div class="input-field-wrapper">
            <!-- 知识库加载开关 -->
            <div class="knowledge-base-switch-compact">
              <span class="switch-label">知识库</span>
              <el-switch
                v-model="enableKnowledgeBase"
                :active-value="true"
                :inactive-value="false"
              />
            </div>
            <div class="input-field">
              <textarea
                v-model="inputMessage"
                @keydown="handleKeyDown"
                @input="adjustInputHeight"
                placeholder="输入消息..."
                class="message-input"
                rows="1"
                ref="messageInput"
              ></textarea>
            </div>
          </div>

          <button
            @click="sendMessage"
            :disabled="!inputMessage.trim() || uploadingFiles.size > 0"
            class="send-btn"
            title="发送消息"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
            >
              <line x1="22" y1="2" x2="11" y2="13"></line>
              <polygon points="22,2 15,22 11,13 2,9 22,2"></polygon>
            </svg>
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, nextTick, onMounted, watch, onUnmounted } from "vue";
import { useRouter } from "vue-router";
import { storeToRefs } from "pinia";
import { useChatStore } from "../stores/chat";
import { useAuthStore } from "../stores/auth";
import { useThemeStore } from "../stores/theme";
import {
  Upload,
  Document,
  Delete,
  Setting,
  User,
  CopyDocument,
  Loading,
  Check,
  Tickets,
  Collection,
} from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { uploadChatFile, sendChatMessage } from "@/api/conversation";
import showdown from "showdown";

// ==================== 初始化 ====================
const router = useRouter();
const chatStore = useChatStore();
const authStore = useAuthStore();
const themeStore = useThemeStore();

// ==================== 常量定义 ====================
const DISPLAY_INTERVAL = 100; // 流式显示更新间隔（ms）
const REQUEST_TIMEOUT = 60000; // 对话接口请求超时时间（ms，1分钟）
const SCROLL_THROTTLE = 50; // 滚动节流时间（ms）
const MAX_FILE_COUNT = 5; // 最大文件数量
const MAX_FILE_SIZE = 100 * 1024 * 1024; // 最大文件大小（100MB）
const ALLOWED_EXTENSIONS = [
  ".doc",
  ".docx",
  ".xls",
  ".xlsx",
  ".pdf",
  ".ppt",
  ".pptx",
  ".txt",
];

// ==================== Store 数据 ====================
// 响应式数据
const {
  conversations,
  currentConversationId,
  currentConversation,
  loading,
  pagination,
  loadingMore,
} = storeToRefs(chatStore);

// 主题相关
const currentTheme = computed(() => themeStore.currentTheme);
const availableThemes = computed(() => themeStore.getAvailableThemes());

// Store 方法
const {
  addConversation,
  deleteConversation,
  switchConversation,
  addMessage,
  clearCurrentConversation,
  loadConversations,
  loadMoreConversations,
  updateTitle,
  updateConversationId,
} = chatStore;

// ==================== 组件状态 ====================
// UI 状态
const sidebarCollapsed = ref(false);
const isMobile = ref(false);
const isSwitchingConversation = ref(false);
const editingConversationId = ref(null);
const editingTitle = ref("");

// 消息输入
const inputMessage = ref("");
const messageInput = ref(null);
const enableKnowledgeBase = ref(true);
const messagesContainer = ref(null);
const conversationListRef = ref(null);

// 文件管理
const selectedFiles = ref([]);
const fileInput = ref(null);
const uploadingFiles = ref(new Set());
let allowClearFiles = false;

// 流式显示
let contentBuffer = "";
let displayTimer = null;
let currentStreamingMessageId = null;

// 其他
const copiedMessageId = ref(null);
let scrollTimer = null;

// ==================== 工具函数 ====================
// 格式化函数
const formatFileSize = (bytes) => {
  if (bytes === 0) return "0 B";
  const k = 1024;
  const sizes = ["B", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
};

// 与 TestMdPreview / MarkdownPlayground 中 Showdown 渲染配置保持一致
const showdownConverter = new showdown.Converter({
  noHeaderId: true,
  smartIndentationFix: true,
  simplifiedAutoLink: false,
  openLinksInNewWindow: false,
  strikethrough: true,
  literalMidWordUnderscores: true,
  tables: true,
  simpleLineBreaks: false,
});

// 与 TestMdPreview 中逻辑一致的标准化处理
const normalizeMarkdownContent = (text = "") => {
  if (!text) return "";
  return String(text)
    .replace(/\r\n/g, "\n")
    .replace(/\r/g, "\n")
    // 将字符串中的字面量 \n 转为真实换行
    .replace(/\\n/g, "\n")
    // 粗体语法后多余空格修正：** xx -> **xx
    .replace(/\*\*\s+/g, "**")
    .replace(/__\s+/g, "__")
    // 标题前后空行兼容
    .replace(/(\*\*.+?\*\*)\n(?=#+)/g, "$1\n\n")
    .replace(/([^\n])\n(?=#+)/g, (_, char) => `${char}\n\n`)
    .replace(/^\s+(?=#+)/gm, "")
    // 列表项前两个以上空格归一为一个空格：-  xx -> - xx
    .replace(/(^|\n)(-\s{2,})/g, (_, prefix) => `${prefix}- `);
};

const formatMessage = (content) => {
  if (!content) return "";

  const source = normalizeMarkdownContent(String(content)).trim();
  if (!source) return "";

  try {
    return showdownConverter.makeHtml(source);
  } catch (error) {
    console.error("Markdown(Showdown) 解析失败，使用兜底渲染", error, {
      content: source.substring(0, 200),
      errorMessage: error.message,
    });
  }

  // 如果解析失败，使用简单的转义和换行处理
  return source
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/\n/g, "<br>");
};

const formatTime = (date) => {
  const now = new Date();
  const diff = now - date;
  if (diff < 60000) return "刚刚";
  if (diff < 3600000) return `${Math.floor(diff / 60000)}分钟前`;
  if (diff < 86400000) return `${Math.floor(diff / 3600000)}小时前`;
  return date.toLocaleDateString();
};

// 工具函数
const getLastMessage = (conversation) => {
  if (!conversation?.messages || conversation.messages.length === 0) return "";
  const lastMessage = conversation.messages[conversation.messages.length - 1];
  if (!lastMessage?.content) return "";
  const content = String(lastMessage.content);
  return content.substring(0, 50) + (content.length > 50 ? "..." : "");
};

const hasValidMessages = (conversation) => {
  if (
    !conversation ||
    !conversation.messages ||
    conversation.messages.length === 0
  ) {
    return false;
  }
  const hasUserMessage = conversation.messages.some(
    (msg) => msg.role === "user" && msg.content && msg.content.trim()
  );
  const hasAiMessage = conversation.messages.some(
    (msg) =>
      (msg.role === "assistant" || msg.role === "ai") &&
      msg.content &&
      msg.content.trim() &&
      !msg.isWaiting
  );
  return hasUserMessage && hasAiMessage;
};

const extractPlainText = (html) => {
  const tempDiv = document.createElement("div");
  tempDiv.innerHTML = formatMessage(html);
  return tempDiv.textContent || tempDiv.innerText || "";
};

// UI 工具函数
const scrollToBottom = () => {
  if (messagesContainer.value) {
    messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
  }
};

const throttledScrollToBottom = () => {
  if (scrollTimer) return;
  scrollTimer = setTimeout(() => {
    scrollToBottom();
    scrollTimer = null;
  }, SCROLL_THROTTLE);
};

const checkMobile = () => {
  isMobile.value = window.innerWidth < 768;
};

// ==================== 流式显示控制 ====================
const startStreamingDisplay = (messageId) => {
  currentStreamingMessageId = messageId;
  contentBuffer = "";
  if (displayTimer) clearInterval(displayTimer);
  displayTimer = setInterval(() => {
    if (contentBuffer && currentStreamingMessageId) {
      const currentMessage = currentConversation.value?.messages?.find(
        (msg) => msg.id === currentStreamingMessageId
      );
      if (currentMessage) {
        currentMessage.content += contentBuffer;
        contentBuffer = "";
        throttledScrollToBottom();
      }
    }
  }, DISPLAY_INTERVAL);
};

const stopStreamingDisplay = () => {
  if (displayTimer) {
    clearInterval(displayTimer);
    displayTimer = null;
  }
  if (contentBuffer && currentStreamingMessageId) {
    const currentMessage = currentConversation.value?.messages?.find(
      (msg) => msg.id === currentStreamingMessageId
    );
    if (currentMessage) {
      currentMessage.content += contentBuffer;
      // 确保 isWaiting 被清除，以便显示复制按钮
      currentMessage.isWaiting = false;
      // 确保消息角色正确
      if (currentMessage.role !== "ai" && currentMessage.role !== "assistant") {
        currentMessage.role = "ai";
      }
      contentBuffer = "";
      nextTick(() => scrollToBottom());
    }
  }
  currentStreamingMessageId = null;
};

// ==================== 对话管理 ====================
const addNewConversation = async () => {
  addConversation();
  await nextTick();
  scrollToBottom();
};

const handleClearConversation = async () => {
  if (currentConversation.value?.messages.length === 0) return;

  try {
    // 使用 Element Plus 的确认对话框
    await ElMessageBox.confirm(
      "确定要清空当前对话吗？此操作不可撤销。",
      "清空确认",
      {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }
    );

    // 保存当前对话ID
    const currentId = currentConversationId.value;

    // 删除当前对话
    await deleteConversation(currentId);

    // 创建新的临时对话（如果已有临时对话，会自动跳转到已有的临时对话）
    addConversation();

    ElMessage.success("清空成功");

    // 等待DOM更新后滚动到底部
    await nextTick();
    scrollToBottom();
  } catch (error) {
    // 如果用户取消，ElMessageBox 会抛出错误，需要区分
    if (error === "cancel" || error === "close") {
      // 用户取消清空，不需要处理
      return;
    }
    console.error("清空对话失败", error);
    ElMessage.error("清空对话失败，请重试");
  }
};

const handleThemeChange = (themeKey) => {
  themeStore.setTheme(themeKey);
  ElMessage.success(`已切换到${themeStore.getCurrentTheme().name}主题`);
};

const handleSettings = (conversationId) => {
  const conversation = conversations.value.find(
    (conv) => conv.id === conversationId
  );
  if (conversation) {
    editingConversationId.value = conversationId;
    editingTitle.value = conversation.title;
    // 等待DOM更新后聚焦输入框
    nextTick(() => {
      // 查找当前编辑的输入框
      const inputElement = document.querySelector(
        `.conversation-item[data-id="${conversationId}"] .title-input input`
      );
      if (inputElement) {
        inputElement.focus();
        inputElement.select();
      }
    });
  }
};

const handleSaveTitle = async (conversationId) => {
  const trimmedTitle = editingTitle.value.trim();
  if (!trimmedTitle) {
    // 如果标题为空，取消编辑
    handleCancelEdit();
    return;
  }

  try {
    await updateTitle(conversationId, trimmedTitle);
    editingConversationId.value = null;
    editingTitle.value = "";
  } catch (error) {
    console.error("保存标题失败", error);
    // 可以显示错误提示
    ElMessage.error("保存标题失败，请重试");
  }
};

const handleCancelEdit = () => {
  editingConversationId.value = null;
  editingTitle.value = "";
};

const handleDeleteConversation = async (conversationId) => {
  const conversation = conversations.value.find(
    (conv) => conv.id === conversationId
  );
  if (!conversation) return;

  // 如果只有一条对话且是前端创建的，但没有有效消息，不允许删除
  if (
    conversations.value.length === 1 &&
    conversation.isFront === true &&
    !hasValidMessages(conversation)
  ) {
    ElMessage.warning("当前对话是临时对话，无法删除");
    return;
  }

  try {
    // 使用 Element Plus 的确认对话框
    await ElMessageBox.confirm(
      `确定要删除对话"${conversation.title}"吗？此操作不可撤销。`,
      "删除确认",
      {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }
    );

    // 用户确认后执行删除
    await deleteConversation(conversationId);
    ElMessage.success("删除成功");
  } catch (error) {
    // 如果用户取消，ElMessageBox 会抛出错误，需要区分
    if (error === "cancel" || error === "close") {
      // 用户取消删除，不需要处理
      return;
    }
    console.error("删除对话失败", error);
    ElMessage.error("删除对话失败，请重试");
  }
};

const handleSwitchConversation = async (conversationId) => {
  if (
    conversationId === currentConversationId.value ||
    isSwitchingConversation.value
  )
    return;

  // 停止流式显示（如果正在显示）
  stopStreamingDisplay();

  isSwitchingConversation.value = true;

  try {
    // 添加切换动画
    const container = messagesContainer.value;
    if (container) {
      container.style.transition = "all 0.2s ease";
      container.style.opacity = "0.3";
      container.style.transform = "translateY(10px)";
    }

    // 短暂延迟让动画生效
    await new Promise((resolve) => setTimeout(resolve, 150));

    // 切换对话时清空已选择的文件
    safeClearSelectedFiles();
    uploadingFiles.value.clear();
    // 切换对话
    switchConversation(conversationId);

    // 等待DOM更新
    await nextTick();

    // 恢复动画
    if (container) {
      container.style.transition = "all 0.3s ease";
      container.style.opacity = "1";
      container.style.transform = "translateY(0)";

      // 滚动到底部
      await nextTick();
      scrollToBottom();
    }
  } finally {
    // 重置切换状态
    setTimeout(() => {
      isSwitchingConversation.value = false;
    }, 300);
  }
};

const toggleSidebar = () => {
  sidebarCollapsed.value = !sidebarCollapsed.value;
};

// ==================== 文件管理 ====================
const safeClearSelectedFiles = () => {
  allowClearFiles = true;
  selectedFiles.value = [];
};

const triggerFileInput = (event) => {
  // 阻止事件冒泡，防止触发其他点击事件
  if (event) {
    event.stopPropagation();
  }

  // 如果没有选择对话，提示用户
  if (!currentConversationId.value) {
    ElMessage.warning("请先选择或创建对话后再上传文件");
    return;
  }

  // 确保文件输入框存在后再触发
  if (fileInput.value) {
    fileInput.value.click();
  }
};

const handleFileSelect = async (event) => {
  // 如果没有选择对话，清空选择并提示
  if (!currentConversationId.value) {
    event.target.value = "";
    ElMessage.warning("请先选择或创建对话后再上传文件");
    return;
  }

  const files = Array.from(event.target.files || []);

  // 如果没有选择文件，直接返回（不处理空选择）
  if (files.length === 0) {
    // 不清空已选择的文件列表，保持现有文件
    return;
  }

  const currentFileCount = selectedFiles.value.length;
  const newFileCount = files.length;
  const totalCount = currentFileCount + newFileCount;

  if (totalCount > MAX_FILE_COUNT) {
    event.target.value = "";
    ElMessage.warning(
      `最多只能上传${MAX_FILE_COUNT}个文件，当前已有${currentFileCount}个文件，本次选择了${newFileCount}个文件`
    );
    return;
  }

  const filesToProcess =
    totalCount > MAX_FILE_COUNT
      ? files.slice(0, MAX_FILE_COUNT - currentFileCount)
      : files;
  if (filesToProcess.length < files.length) {
    ElMessage.warning(
      `最多只能上传${MAX_FILE_COUNT}个文件，已自动选择前${filesToProcess.length}个文件`
    );
  }

  // 逐个验证和上传文件
  for (const file of filesToProcess) {
    // 先验证文件
    if (!validateFile(file)) {
      continue; // 验证失败，跳过该文件
    }
    await uploadFile(file);
  }

  // 清空input值，允许重复选择同一文件
  // 使用nextTick确保文件上传完成后再清空
  await nextTick();
  if (event.target) {
    event.target.value = "";
  }
};

const validateFile = (file) => {
  if (file.size > MAX_FILE_SIZE) {
    ElMessage.error(`文件 "${file.name}" 大小不能超过 100MB`);
    return false;
  }
  const fileName = file.name.toLowerCase();
  const fileExtension = fileName.substring(fileName.lastIndexOf("."));
  if (!ALLOWED_EXTENSIONS.includes(fileExtension)) {
    ElMessage.error(
      `文件 "${file.name}" 格式不支持，请上传 word/excel/pdf/ppt/txt 格式的文件`
    );
    return false;
  }
  return true;
};

const uploadFile = async (file) => {
  if (!currentConversationId.value) {
    ElMessage.warning("请先选择或创建对话后再上传文件");
    return;
  }

  // 添加到上传中列表
  uploadingFiles.value.add(file.name);

  try {
    // 调用上传接口
    const response = await uploadChatFile(currentConversationId.value, file);

    // 上传成功，添加到已选择文件列表
    // 如果后端没有返回filename，使用原始文件名
    const filename = response.filename || file.name;
    const uploadedFile = {
      id: response.id,
      filename: filename,
      file_type: response.file_type || file.type || "",
      filepath: response.filepath || "",
      uploaded_at: response.uploaded_at || new Date().toISOString(),
      is_active: response.is_active !== undefined ? response.is_active : true,
      name: filename, // 用于显示，优先使用后端返回的，否则使用原始文件名
      size: file.size, // 保留原始文件大小用于显示
    };

    selectedFiles.value.push(uploadedFile);
    ElMessage.success(`文件 "${file.name}" 上传成功`);

    // 注意：文件上传后不立即显示在对话区域，只有点击发送按钮后才会显示
  } catch (error) {
    console.error("文件上传失败", error);
    ElMessage.error(`文件 "${file.name}" 上传失败，请重试`);
  } finally {
    // 从上传中列表移除
    uploadingFiles.value.delete(file.name);
  }
};

const removeFile = (index) => {
  selectedFiles.value.splice(index, 1);
};

// ==================== 消息处理 ====================
const getFriendlyErrorMessage = (errorMessage) => {
  // 统一超时错误提示
  if (
    errorMessage.includes("超时") ||
    errorMessage.includes("timeout") ||
    errorMessage.includes("服务器繁忙")
  ) {
    return "服务器繁忙，请您稍后重试！";
  } else if (
    errorMessage.includes("网络") ||
    errorMessage.includes("network")
  ) {
    return "🌐 抱歉，网络连接出现问题。\n\n请检查您的网络连接是否正常，然后重试。";
  } else if (errorMessage.includes("401") || errorMessage.includes("未授权")) {
    return "🔒 抱歉，您的登录已过期。\n\n请重新登录后继续使用。";
  }
  return `😔 抱歉，遇到了问题：${errorMessage}\n\n请稍后重试，如果问题持续存在，请联系技术支持。`;
};

const handleMessageError = (messageId, error, hasContent = false) => {
  const currentMessage = currentConversation.value?.messages?.find(
    (msg) => msg.id === messageId
  );
  if (!currentMessage) return;

  currentMessage.isWaiting = false;
  const errorMessage = error?.message || "发送消息失败，请重试";

  if (!hasContent && !currentMessage.content?.trim()) {
    currentMessage.content = getFriendlyErrorMessage(errorMessage);
  } else if (hasContent) {
    currentMessage.content += `\n\n---\n\n⚠️ 注意：在回答过程中遇到了问题：${errorMessage}`;
  }

  nextTick(() => scrollToBottom());
};

const createTimeoutHandler = (messageId, timeout = REQUEST_TIMEOUT) => {
  return setTimeout(() => {
    stopStreamingDisplay();
    const currentMessage = currentConversation.value?.messages?.find(
      (msg) => msg.id === messageId
    );
    if (currentMessage) {
      currentMessage.isWaiting = false;
      if (!currentMessage.content?.trim()) {
        currentMessage.content = "服务器繁忙，请您稍后重试！";
      } else {
        currentMessage.content +=
          "\n\n---\n\n⚠️ 注意：响应在传输过程中中断，可能不完整。";
      }
      nextTick(() => scrollToBottom());
    }
    // 注意：这里不显示错误弹窗，由 onError 回调统一处理，避免重复提示
  }, timeout);
};

const sendMessage = async () => {
  if (!currentConversationId.value) {
    ElMessage.warning("请先选择或创建对话后再发送消息");
    return;
  }
  // 必须要有文字输入才能发送（即使有文件也必须输入文字）
  if (!inputMessage.value.trim()) {
    ElMessage.warning("请输入问题后再发送");
    return;
  }

  // 准备要发送的文件列表
  const filesToSend = selectedFiles.value.map((file) => ({
    id: file.id,
    name: file.name || file.filename,
    size: file.size,
    type: file.file_type,
    filepath: file.filepath,
  }));

  const message = {
    role: "user",
    content: inputMessage.value.trim(),
    files: filesToSend,
  };

  // 添加用户消息（包含文字和文件）
  addMessage(message);

  inputMessage.value = "";
  safeClearSelectedFiles();

  // 滚动到底部
  await nextTick();
  scrollToBottom();

  // 保存当前对话ID（可能是临时ID或后端ID）
  const currentConvId = currentConversationId.value;
  const isFrontendId = currentConversation.value?.isFront === true;

  // 准备文件ID数组
  const fileIds = filesToSend
    .map((file) => file.id)
    .filter((id) => id !== undefined);

  const aiMessageId = `ai-${Date.now()}`;
  const aiResponse = {
    role: "ai",
    content: "",
    timestamp: new Date(),
    id: aiMessageId,
    isWaiting: true,
  };
  addMessage(aiResponse);

  let requestTimeoutTimer = createTimeoutHandler(aiMessageId, REQUEST_TIMEOUT);
  let hasShownError = false; // 标记是否已显示错误，避免重复提示
  let hasErrorOccurred = false; // 一旦出错（超时/服务器繁忙等），后续流式内容不再展示

  try {
    let returnedConversationId = null;
    let hasReceivedContent = false;

    await sendChatMessage(
      currentConvId, // 使用当前对话ID（可能是临时ID或后端ID）
      message.content,
      fileIds,
      enableKnowledgeBase.value, // 是否加载知识库
      (chunk) => {
        if (hasErrorOccurred) {
          // 已经发生错误（如服务器繁忙），忽略后续所有 chunk，避免再次刷新页面内容
          return;
        }
        if (requestTimeoutTimer) {
          clearTimeout(requestTimeoutTimer);
          requestTimeoutTimer = createTimeoutHandler(
            aiMessageId,
            REQUEST_TIMEOUT
          );
        }
        let contentToAdd = "";
        if (chunk.content !== undefined) {
          contentToAdd = chunk.content || "";
        } else if (typeof chunk === "string") {
          contentToAdd = chunk;
        } else if (chunk.text !== undefined) {
          contentToAdd = chunk.text || "";
        } else if (chunk.message !== undefined) {
          contentToAdd = chunk.message || "";
        } else if (chunk.data !== undefined) {
          contentToAdd =
            typeof chunk.data === "string"
              ? chunk.data
              : chunk.data.content || "";
        }
        if (contentToAdd) {
          hasReceivedContent = true;
          if (!currentStreamingMessageId) {
            startStreamingDisplay(aiMessageId);
            const currentMessage = currentConversation.value?.messages?.find(
              (msg) => msg.id === aiMessageId
            );
            if (currentMessage) {
              currentMessage.isWaiting = false;
              if (!currentMessage.content) currentMessage.content = "";
              // 确保消息角色正确（统一为 'ai'）
              if (
                currentMessage.role !== "ai" &&
                currentMessage.role !== "assistant"
              ) {
                currentMessage.role = "ai";
              }
            }
          }
          contentBuffer += contentToAdd;
        }
      },
      (returnedConversationId) => {
        if (hasErrorOccurred) {
          return;
        }
        if (requestTimeoutTimer) {
          clearTimeout(requestTimeoutTimer);
          requestTimeoutTimer = null;
        }
        stopStreamingDisplay();
        nextTick(() => {
          setTimeout(() => {
            const currentMessage = currentConversation.value?.messages?.find(
              (msg) => msg.id === aiMessageId
            );
            if (currentMessage) {
              // 确保消息角色正确
              if (
                currentMessage.role !== "ai" &&
                currentMessage.role !== "assistant"
              ) {
                currentMessage.role = "ai";
              }
              if (!hasReceivedContent && !currentMessage.content?.trim()) {
                currentMessage.isWaiting = false;
                currentMessage.content =
                  "😔 抱歉，未能获取到回答内容。\n\n可能是网络问题或服务暂时不可用，请稍后重试。如果问题持续存在，请联系技术支持。";
              } else if (hasReceivedContent) {
                currentMessage.isWaiting = false;
                // 确保内容不为空时才显示复制按钮
                if (currentMessage.content && currentMessage.content.trim()) {
                  // 强制触发响应式更新
                  currentMessage.content = currentMessage.content;
                }
              }
            }
          }, 200);
        });
        if (
          isFrontendId &&
          returnedConversationId &&
          returnedConversationId !== currentConvId
        ) {
          const isFrontendIdFormat =
            /^[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}$/i.test(
              String(currentConvId)
            );
          if (isFrontendIdFormat) {
            updateConversationId(currentConvId, returnedConversationId);
          }
        }
        nextTick(() => scrollToBottom());
      },
      (error) => {
        if (requestTimeoutTimer) {
          clearTimeout(requestTimeoutTimer);
          requestTimeoutTimer = null;
        }
         stopStreamingDisplay();
         hasErrorOccurred = true;
        console.error("发送消息失败", error);
        // 统一处理超时错误提示
        let errorMessage = error.message || "发送消息失败，请重试";
        if (
          error.message?.includes("timeout") ||
          error.message?.includes("超时") ||
          error.message?.includes("服务器繁忙")
        ) {
          errorMessage = "服务器繁忙，请您稍后重试！";
        }
        // 只显示一次错误弹窗（避免重复提示）
        if (!hasShownError) {
          ElMessage.error(errorMessage);
          hasShownError = true;
        }
        const currentMessage = currentConversation.value?.messages?.find(
          (msg) => msg.id === aiMessageId
        );
        if (currentMessage) {
          handleMessageError(
            aiMessageId,
            { message: errorMessage },
            !!currentMessage.content?.trim()
          );
        }
      }
    );
  } catch (error) {
    hasErrorOccurred = true;
    if (requestTimeoutTimer) {
      clearTimeout(requestTimeoutTimer);
      requestTimeoutTimer = null;
    }
    stopStreamingDisplay();
    console.error("发送消息失败", error);
    // 统一处理超时错误提示
    let errorMessage = error.message || "发送消息失败，请重试";
    if (
      error.message?.includes("timeout") ||
      error.message?.includes("超时") ||
      error.message?.includes("服务器繁忙")
    ) {
      errorMessage = "服务器繁忙，请您稍后重试！";
    } else if (
      error.message?.includes("network") ||
      error.message?.includes("网络")
    ) {
      errorMessage = "网络连接异常，请检查网络后重试";
    }
    // 只显示一次错误弹窗（避免重复提示）
    if (!hasShownError) {
      ElMessage.error(errorMessage);
      hasShownError = true;
    }
    handleMessageError(aiMessageId, { message: errorMessage });
  }
};

// ==================== UI 交互 ====================
const handleKeyDown = (event) => {
  if (event.key === "Enter" && !event.shiftKey) {
    event.preventDefault();
    sendMessage();
  }
};

const copyMessageContent = async (content, messageId) => {
  const plainText = extractPlainText(content);
  try {
    await navigator.clipboard.writeText(plainText);
    copiedMessageId.value = messageId;
    ElMessage.success("内容已复制到剪贴板");
    setTimeout(() => {
      if (copiedMessageId.value === messageId) copiedMessageId.value = null;
    }, 2000);
  } catch (error) {
    console.error("复制失败", error);
    // 降级方案
    try {
      const textArea = document.createElement("textarea");
      textArea.value = plainText;
      textArea.style.position = "fixed";
      textArea.style.opacity = "0";
      document.body.appendChild(textArea);
      textArea.select();
      document.execCommand("copy");
      document.body.removeChild(textArea);
      copiedMessageId.value = messageId;
      ElMessage.success("内容已复制到剪贴板");
      setTimeout(() => {
        if (copiedMessageId.value === messageId) copiedMessageId.value = null;
      }, 2000);
    } catch (fallbackError) {
      console.error("降级复制方案也失败", fallbackError);
      ElMessage.error("复制失败，请重试");
    }
  }
};

const adjustInputHeight = () => {
  if (messageInput.value) {
    messageInput.value.style.height = "auto";
    messageInput.value.style.height =
      Math.min(messageInput.value.scrollHeight, 120) + "px";
  }
};

const goToKnowledgeBase = () => {
  router.push("/knowledge-base");
};

const goToTestMdPreview = () => {
  router.push("/test-md-preview");
};

const goToMarkdownPlayground = () => {
  router.push("/markdown-playground");
};

const handleConversationListScroll = async (event) => {
  const target = event.target;
  if (!target) return;
  const { scrollTop, scrollHeight, clientHeight } = target;
  const isNearBottom = scrollHeight - scrollTop - clientHeight < 100;
  if (
    isNearBottom &&
    pagination.value.hasNext &&
    !loadingMore.value &&
    !loading.value
  ) {
    try {
      await loadMoreConversations();
    } catch (error) {
      console.error("自动加载更多失败", error);
    }
  }
};

const handleLogout = async () => {
  try {
    await ElMessageBox.confirm("确定要退出登录吗？", "退出确认", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    });
    authStore.logout();
    localStorage.removeItem("ai-chat-current-conversation-id");
    router.push("/login");
    ElMessage.success("已退出登录");
  } catch (error) {
    if (error !== "cancel" && error !== "close") {
      console.error("退出登录失败", error);
    }
  }
};

// ==================== 生命周期 ====================
onMounted(async () => {
  checkMobile();
  window.addEventListener("resize", checkMobile);
  await loadConversations();
  await nextTick();
  scrollToBottom();
});

onUnmounted(() => {
  window.removeEventListener("resize", checkMobile);
  stopStreamingDisplay();
});

// ==================== Watchers ====================
watch(inputMessage, adjustInputHeight);

watch(
  selectedFiles,
  (newVal, oldVal) => {
    if (
      oldVal &&
      oldVal.length > 0 &&
      newVal.length === 0 &&
      !allowClearFiles
    ) {
      console.warn("检测到文件列表被意外清空，正在恢复...");
      nextTick(() => {
        selectedFiles.value = [...oldVal];
      });
    }
    allowClearFiles = false;
  },
  { deep: true }
);

watch(
  () => currentConversation.value?.messages,
  () => {
    if (currentConversation.value?.messages) {
      nextTick(() => scrollToBottom());
    }
  },
  { deep: true }
);
</script>

<style scoped>
.chat-container {
  display: flex;
  height: 100vh;
  background: var(
    --theme-gradient,
    linear-gradient(135deg, #667eea 0%, #764ba2 100%)
  );
  position: relative;
  overflow: hidden;
}

.chat-container::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(10px);
  z-index: 0;
}

.chat-container > * {
  position: relative;
  z-index: 1;
}

/* 左侧边栏 */
.sidebar {
  width: 300px;
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 20px;
  margin: 10px 0 10px 10px;
  display: flex;
  flex-direction: column;
  transition: all 0.3s ease;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
  overflow: hidden;
}

.sidebar-collapsed {
  width: 60px;
}

.sidebar-collapsed .conversation-list,
.sidebar-collapsed .new-chat-btn span {
  display: none;
}

.sidebar-collapsed .header-content h2 {
  display: none;
}

.sidebar-collapsed .header-content {
  flex-direction: column;
  gap: 10px;
  margin-bottom: 0;
}

.sidebar-collapsed .sidebar-toggle {
  padding: 10px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.05);
  transition: all 0.2s ease;
}

.sidebar-collapsed .sidebar-toggle:hover {
  background: rgba(0, 0, 0, 0.1);
  transform: scale(1.05);
}

.sidebar-collapsed .new-chat-btn {
  width: 40px;
  height: 40px;
  padding: 0;
  border-radius: 50%;
  margin: 0 auto;
  justify-content: center;
  align-items: center;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
  transition: all 0.3s ease;
}

.sidebar-collapsed .new-chat-btn:hover {
  transform: scale(1.1);
  box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}

.sidebar-collapsed .new-chat-btn svg {
  width: 20px;
  height: 20px;
  color: white;
}

.collapsed-hint {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px 10px;
  text-align: center;
  color: #666;
  cursor: pointer;
  transition: all 0.2s ease;
  border-radius: 12px;
  margin: 10px;
}

.collapsed-hint:hover {
  background: rgba(0, 0, 0, 0.05);
  color: #333;
}

.hint-icon {
  font-size: 24px;
  margin-bottom: 8px;
  animation: pulse 2s infinite;
}

@keyframes pulse {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.1);
  }
}

.hint-text {
  font-size: 11px;
  line-height: 1.3;
  font-weight: 500;
}

.sidebar-header {
  padding: 20px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}

.sidebar-collapsed .sidebar-header {
  padding: 15px 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 15px;
}

.header-content {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 15px;
}

.header-content h2 {
  margin: 0;
  color: #333;
  font-size: 18px;
  font-weight: 600;
}

.sidebar-toggle {
  background: none;
  border: none;
  cursor: pointer;
  padding: 8px;
  border-radius: 6px;
  color: #666;
  transition: all 0.2s ease;
}

.sidebar-toggle:hover {
  background-color: #f0f0f0;
  color: #333;
}

.sidebar-toggle svg {
  width: 20px;
  height: 20px;
}

.new-chat-btn {
  width: 100%;
  padding: 12px;
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border-radius: 8px;
  transition: all 0.2s ease;
}

.new-chat-btn svg {
  width: 16px;
  height: 16px;
}

.conversation-list {
  flex: 1;
  overflow-y: auto;
  padding: 10px;
  display: flex;
  flex-direction: column;
}

.conversation-list :deep(.el-empty) {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: 40px 20px;
}

.conversation-list :deep(.el-empty__description) {
  color: #999;
  font-size: 14px;
}

.loading-more,
.no-more {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  color: #909399;
  font-size: 12px;
  gap: 8px;
}

.loading-more .el-icon {
  font-size: 16px;
  animation: rotating 1s linear infinite;
}

@keyframes rotating {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.conversation-item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px;
  margin-bottom: 8px;
  border-radius: 12px;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  border: 1px solid transparent;
  transform: translateX(0);
  opacity: 1;
}

.conversation-item:hover {
  background-color: rgba(0, 123, 255, 0.1);
  transform: translateX(4px);
}

.conversation-item.active {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
  transform: translateX(4px);
}

.conversation-item.active .conversation-title,
.conversation-item.active .conversation-preview,
.conversation-item.active .conversation-time {
  color: white;
}

.conversation-item.switching {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  transform: scale(0.98);
  box-shadow: 0 2px 10px rgba(102, 126, 234, 0.3);
}

.conversation-item.switching .conversation-title,
.conversation-item.switching .conversation-preview,
.conversation-item.switching .conversation-time {
  color: white;
}

.conversation-item:active {
  transform: scale(0.95);
  transition: transform 0.1s ease;
}

.conversation-content {
  flex: 1;
  min-width: 0;
}

.conversation-title {
  font-size: 14px;
  color: #333;
  margin-bottom: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 500;
}

.title-input {
  margin-bottom: 4px;
}

.title-input :deep(.el-input__wrapper) {
  padding: 4px 8px;
  box-shadow: 0 0 0 1px #dcdfe6 inset;
}

.title-input :deep(.el-input__wrapper:hover) {
  box-shadow: 0 0 0 1px #c0c4cc inset;
}

.title-input :deep(.el-input__wrapper.is-focus) {
  box-shadow: 0 0 0 1px #409eff inset;
}

.conversation-preview {
  font-size: 12px;
  color: #666;
  margin-bottom: 4px;
  line-height: 1.4;
  white-space: normal;
  max-height: calc(1.4em * 2);
  display: -webkit-box;
  line-clamp: 2;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}

.conversation-time {
  font-size: 11px;
  color: #999;
}

.conversation-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  margin-left: auto;
  align-self: center;
}

.settings-btn,
.delete-btn {
  background: none;
  border: none;
  color: #999;
  cursor: pointer;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  transition: all 0.2s ease;
  padding: 0;
}

.settings-btn .el-icon,
.delete-btn .el-icon {
  font-size: 16px;
  width: 16px;
  height: 16px;
}

.settings-btn:hover {
  background-color: rgba(102, 126, 234, 0.1);
  color: #667eea;
}

.delete-btn:hover:not(.disabled) {
  background-color: #ff4444;
  color: white;
}

.delete-btn.disabled {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

.conversation-item.active .settings-btn,
.conversation-item.active .delete-btn {
  color: rgba(255, 255, 255, 0.9);
}

.conversation-item.active .settings-btn:hover {
  background-color: rgba(255, 255, 255, 0.25);
  color: #ffffff;
}

.conversation-item.active .delete-btn:hover:not(.disabled) {
  background-color: rgba(255, 255, 255, 0.3);
  color: #ffffff;
}

/* 右侧主内容 */
.main-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: var(--theme-background, rgba(255, 255, 255, 0.95));
  backdrop-filter: blur(20px);
  border-radius: 20px;
  margin: 10px;
  border: 1px solid var(--theme-border, rgba(255, 255, 255, 0.2));
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
  overflow: hidden;
}

/* 页面标题栏 */
.page-title-bar {
  padding: 24px 20px 20px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  background: linear-gradient(180deg, #1e40af 0%, #2563eb 100%);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.title-content {
  display: flex;
  align-items: center;
  gap: 20px;
}

.logo-icon {
  width: 60px;
  height: 60px;
  object-fit: contain;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.95);
  padding: 4px;
  backdrop-filter: blur(10px);
  border: 2px solid rgba(255, 255, 255, 0.3);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  flex-shrink: 0;
}

.main-title {
  margin: 0;
  font-size: 28px;
  font-weight: 700;
  color: #ffffff;
  letter-spacing: 1px;
  white-space: nowrap;
}

.chat-header {
  padding: 16px 20px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: rgba(255, 255, 255, 0.8);
  backdrop-filter: blur(10px);
}

.header-left {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
  min-width: 0; /* 允许文本截断 */
}

.header-left h3 {
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

.mobile-sidebar-toggle {
  background: rgba(255, 255, 255, 0.2);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 8px;
  cursor: pointer;
  padding: 8px;
  color: #ffffff;
  transition: all 0.2s ease;
}

.mobile-sidebar-toggle:hover {
  background-color: rgba(255, 255, 255, 0.3);
  color: #ffffff;
}

.mobile-sidebar-toggle svg {
  width: 20px;
  height: 20px;
}

.chat-header h3 {
  margin: 0;
  color: var(--theme-text, #333);
  font-size: 18px;
  font-weight: 600;
}

.header-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-shrink: 0; /* 防止按钮被压缩 */
}

.theme-btn-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 16px;
  background: var(--theme-surface, #f5f7fa);
  color: var(--theme-text, #303133);
  border: 1px solid var(--theme-border, #dcdfe6);
  border-radius: 20px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.3s ease;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.theme-btn-header:hover {
  background: var(--theme-hover, #ecf5ff);
  border-color: var(--theme-primary, #409eff);
  color: var(--theme-primary, #409eff);
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
}

.theme-btn-header:active {
  transform: translateY(0);
}

.theme-btn-header .el-icon {
  font-size: 18px;
}

.knowledge-base-btn-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 16px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border: none;
  border-radius: 20px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.3s ease;
  box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}

.knowledge-base-btn-header:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}

.knowledge-base-btn-header:active {
  transform: translateY(0);
}

.knowledge-base-btn-header .el-icon {
  font-size: 18px;
}

.test-page-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 9px 14px;
  background: linear-gradient(135deg, #38bdf8 0%, #3b82f6 100%);
  color: #fff;
  border: none;
  border-radius: 16px;
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  transition: all 0.2s ease;
  box-shadow: 0 3px 10px rgba(56, 189, 248, 0.35);
}

.test-page-btn.alt {
  background: linear-gradient(135deg, #34d399 0%, #059669 100%);
  box-shadow: 0 3px 10px rgba(5, 150, 105, 0.35);
}

.test-page-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(56, 189, 248, 0.35);
}

.test-page-btn.alt:hover {
  box-shadow: 0 6px 18px rgba(5, 150, 105, 0.35);
}

.test-page-btn:active {
  transform: translateY(0);
}

.test-page-btn .el-icon {
  font-size: 17px;
}

.logout-btn-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 16px;
  background: linear-gradient(135deg, #f56565 0%, #c53030 100%);
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.3s ease;
  box-shadow: 0 2px 8px rgba(245, 101, 101, 0.3);
}

.logout-btn-header:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(245, 101, 101, 0.4);
}

.logout-btn-header:active {
  transform: translateY(0);
}

.logout-btn-header .el-icon {
  font-size: 18px;
}

.clear-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
  color: white;
  border: none;
  border-radius: 20px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.3s ease;
  box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
  position: relative;
  overflow: hidden;
}

.clear-btn::before {
  content: "";
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255, 255, 255, 0.2),
    transparent
  );
  transition: left 0.5s;
}

.clear-btn:hover::before {
  left: 100%;
}

.clear-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(255, 107, 107, 0.4);
}

.clear-btn:active {
  transform: translateY(0);
  box-shadow: 0 2px 10px rgba(255, 107, 107, 0.3);
}

.clear-btn:disabled {
  background: linear-gradient(135deg, #ccc 0%, #bbb 100%);
  cursor: not-allowed;
  transform: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  opacity: 0.6;
}

.clear-btn:disabled:hover {
  transform: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.clear-btn .el-icon {
  font-size: 16px;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

.btn-text {
  font-weight: 500;
  letter-spacing: 0.5px;
}

.messages-container {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  opacity: 1;
  transform: translateY(0);
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: #666;
  text-align: center;
  padding: 40px;
}

.empty-icon {
  margin-bottom: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.slogan-text {
  font-size: 36px;
  font-weight: 700;
  color: #667eea;
  letter-spacing: 4px;
  text-shadow: 2px 2px 4px rgba(102, 126, 234, 0.2);
  animation: fadeIn 1s ease-in;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.empty-state h3 {
  margin: 0 0 12px 0;
  color: #333;
  font-size: 24px;
  font-weight: 600;
}

.empty-state p {
  margin: 0 0 32px 0;
  font-size: 16px;
  color: #666;
  line-height: 1.5;
}

.messages {
  max-width: 800px;
  margin: 0 auto;
}

.message {
  display: flex;
  margin-bottom: 24px;
  align-items: flex-start;
  animation: slideIn 0.3s ease-out;
}

@keyframes slideIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.message.user {
  flex-direction: row-reverse;
}

.message-avatar {
  width: 44px;
  height: 44px;
  margin: 0 12px;
  flex-shrink: 0;
}

.avatar-icon {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  transition: all 0.2s ease;
}

.avatar-icon .user-icon {
  width: 24px;
  height: 24px;
  display: block;
}

.avatar-icon .ai-avatar-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  display: block;
}

.avatar-icon.user {
  background: var(
    --theme-gradient,
    linear-gradient(135deg, #667eea 0%, #764ba2 100%)
  );
  color: white;
  box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}

.avatar-icon.ai {
  background: var(
    --theme-ai-gradient,
    linear-gradient(135deg, #f093fb 0%, #f5576c 100%)
  );
  color: white;
  box-shadow: 0 4px 15px rgba(240, 147, 251, 0.3);
  padding: 0;
  overflow: hidden;
}

.message-content {
  flex: 1;
  min-width: 0;
}

.message-bubble {
  max-width: 100%;
  padding: 16px 20px;
  border-radius: 20px;
  position: relative;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  transition: all 0.2s ease;
}

.message-bubble:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
}

/* 悬停到整个消息内容区域时显示复制按钮 */
.message-content:hover .copy-button,
.message-bubble:hover .copy-button {
  opacity: 1;
  visibility: visible;
}

.message.user .message-bubble {
  background: var(
    --theme-gradient,
    linear-gradient(135deg, #667eea 0%, #764ba2 100%)
  );
  color: white;
  margin-left: auto;
  border-bottom-right-radius: 6px;
}

.message.ai .message-bubble {
  background: var(--theme-surface, #f5f7fa);
  color: var(--theme-text, #333);
  border: 1px solid var(--theme-border, rgba(0, 0, 0, 0.05));
  border-bottom-left-radius: 6px;
}

.typing-bubble {
  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  border: 1px solid rgba(0, 0, 0, 0.05);
  border-bottom-left-radius: 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 16px 20px;
}

.typing-text {
  font-size: 13px;
  color: #666;
  font-weight: 500;
  margin-top: 4px;
}

.message-files {
  margin-bottom: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
}

.file-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: rgba(102, 126, 234, 0.1);
  border-radius: 10px;
  border: 1px solid rgba(102, 126, 234, 0.2);
  font-size: 14px;
  transition: all 0.2s ease;
}

.file-item:hover {
  background: rgba(102, 126, 234, 0.15);
  border-color: rgba(102, 126, 234, 0.3);
}

.file-item svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: #667eea;
}

.file-name-text {
  flex: 1;
  font-weight: 500;
  color: #333;
  word-break: break-all;
}

.message-bubble.user .file-item {
  background: rgba(102, 126, 234, 0.1);
  border-color: rgba(102, 126, 234, 0.2);
}

.message-bubble.ai .file-item {
  background: rgba(0, 0, 0, 0.05);
  border-color: rgba(0, 0, 0, 0.1);
}

.message-bubble.ai .file-item svg {
  color: #666;
}

.file-size {
  font-size: 12px;
  color: #999;
  margin-left: 4px;
}

.message-time-only {
  font-size: 11px;
  color: #999;
  margin-top: 4px;
  text-align: right;
  padding-right: 4px;
}

.message-text {
  line-height: 1.5;
  word-wrap: break-word;
  font-size: 15px;
  padding-right: 50px; /* 为复制按钮留出空间，避免文字重叠 */
}

/* 空内容友好提示样式 */
.message-empty-hint {
  padding: 16px 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 60px;
}

.empty-hint-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: #999;
  font-size: 14px;
  text-align: center;
  line-height: 1.6;
}

.empty-hint-icon {
  font-size: 24px;
  opacity: 0.7;
}

.empty-hint-text {
  max-width: 300px;
  word-wrap: break-word;
}

.message.user .empty-hint-content {
  color: rgba(255, 255, 255, 0.8);
}

.message.ai .empty-hint-content {
  color: #666;
}

.message-text code {
  background: rgba(0, 0, 0, 0.1);
  padding: 2px 6px;
  border-radius: 4px;
  font-family: "Courier New", monospace;
  font-size: 13px;
}

.message.user .message-text code {
  background: rgba(255, 255, 255, 0.2);
}

/* Markdown样式 */
.message-text :deep(h1),
.message-text :deep(h2),
.message-text :deep(h3),
.message-text :deep(h4),
.message-text :deep(h5),
.message-text :deep(h6) {
  margin-top: 16px;
  margin-bottom: 8px;
  font-weight: 600;
  line-height: 1.4;
}

.message-text :deep(h1) {
  font-size: 24px;
  border-bottom: 2px solid rgba(0, 0, 0, 0.1);
  padding-bottom: 8px;
}

.message-text :deep(h2) {
  font-size: 20px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  padding-bottom: 6px;
}

.message-text :deep(h3) {
  font-size: 18px;
}

.message-text :deep(h4) {
  font-size: 16px;
}

.message-text :deep(h5) {
  font-size: 14px;
}

.message-text :deep(h6) {
  font-size: 13px;
  color: #666;
}

.message.user .message-text :deep(h1),
.message.user .message-text :deep(h2),
.message.user .message-text :deep(h3),
.message.user .message-text :deep(h4),
.message.user .message-text :deep(h5),
.message.user .message-text :deep(h6) {
  color: rgba(255, 255, 255, 0.95);
}

.message-text :deep(p) {
  margin: 8px 0;
  line-height: 1.6;
}

.message-text :deep(ul),
.message-text :deep(ol) {
  margin: 8px 0;
  padding-left: 24px;
  line-height: 1.6;
}

.message-text :deep(li) {
  margin: 4px 0;
}

.message-text :deep(blockquote) {
  margin: 12px 0;
  padding: 8px 16px;
  border-left: 4px solid #409eff;
  background: rgba(64, 158, 255, 0.05);
  border-radius: 4px;
  color: #666;
}

.message.user .message-text :deep(blockquote) {
  background: rgba(255, 255, 255, 0.1);
  border-left-color: rgba(255, 255, 255, 0.5);
  color: rgba(255, 255, 255, 0.9);
}

.message-text :deep(pre) {
  margin: 12px 0;
  padding: 12px 16px;
  background: rgba(0, 0, 0, 0.05);
  border-radius: 6px;
  overflow-x: auto;
  font-family: "Courier New", "Consolas", "Monaco", monospace;
  font-size: 13px;
  line-height: 1.5;
}

.message.user .message-text :deep(pre) {
  background: rgba(255, 255, 255, 0.1);
}

.message-text :deep(pre code) {
  background: transparent;
  padding: 0;
  font-size: inherit;
  color: inherit;
}

.message-text :deep(table) {
  width: 100%;
  border-collapse: collapse;
  margin: 12px 0;
  font-size: 14px;
}

.message-text :deep(table th),
.message-text :deep(table td) {
  border: 1px solid rgba(0, 0, 0, 0.1);
  padding: 8px 12px;
  text-align: left;
}

.message-text :deep(table th) {
  background: rgba(0, 0, 0, 0.05);
  font-weight: 600;
}

.message.user .message-text :deep(table th),
.message.user .message-text :deep(table td) {
  border-color: rgba(255, 255, 255, 0.2);
}

.message.user .message-text :deep(table th) {
  background: rgba(255, 255, 255, 0.15);
}

.message-text :deep(hr) {
  margin: 16px 0;
  border: none;
  border-top: 1px solid rgba(0, 0, 0, 0.1);
}

.message.user .message-text :deep(hr) {
  border-top-color: rgba(255, 255, 255, 0.2);
}

.message-text :deep(a) {
  color: #409eff;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: all 0.2s;
}

.message-text :deep(a:hover) {
  border-bottom-color: #409eff;
}

.message.user .message-text :deep(a) {
  color: rgba(255, 255, 255, 0.9);
}

.message.user .message-text :deep(a:hover) {
  border-bottom-color: rgba(255, 255, 255, 0.9);
}

.message-text :deep(img) {
  max-width: 100%;
  height: auto;
  border-radius: 6px;
  margin: 12px 0;
}

.message-text :deep(strong) {
  font-weight: 600;
}

.message-text :deep(em) {
  font-style: italic;
}

.message-time {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.7);
  margin-top: 8px;
  text-align: right;
}

.message.ai .message-time {
  color: #999;
  text-align: left;
}

/* 复制按钮样式 */
.copy-button {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(64, 158, 255, 0.15);
  border: 1.5px solid rgba(64, 158, 255, 0.4);
  border-radius: 8px;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transition: all 0.2s ease;
  z-index: 10;
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
  pointer-events: auto;
}

.copy-button:hover {
  background: rgba(64, 158, 255, 0.25);
  border-color: rgba(64, 158, 255, 0.6);
  transform: scale(1.1);
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
  opacity: 1;
}

.copy-button:active {
  transform: scale(0.95);
}

.copy-button .el-icon {
  font-size: 16px;
  color: #409eff;
  font-weight: 500;
}

.copy-button:hover .el-icon {
  color: #337ecc;
}

.copy-tip {
  position: absolute;
  top: -30px;
  right: 0;
  background: rgba(0, 0, 0, 0.8);
  color: white;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
  white-space: nowrap;
  pointer-events: none;
}

.copy-tip::after {
  content: "";
  position: absolute;
  bottom: -4px;
  right: 12px;
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 4px solid rgba(0, 0, 0, 0.8);
}

/* 打字指示器 */
.typing-indicator {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0;
}

/* AI正在思考提示样式 */
.message-thinking {
  padding: 12px 16px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.thinking-text {
  display: flex;
  align-items: center;
  gap: 6px;
  color: #666;
  font-size: 14px;
}

.thinking-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #999;
  animation: thinking-pulse 1.4s infinite ease-in-out;
}

.thinking-dot:nth-child(1) {
  animation-delay: -0.32s;
}

.thinking-dot:nth-child(2) {
  animation-delay: -0.16s;
}

.thinking-dot:nth-child(3) {
  animation-delay: 0s;
}

.thinking-label {
  margin-left: 4px;
  font-weight: 500;
  color: #666;
}

@keyframes thinking-pulse {
  0%,
  80%,
  100% {
    transform: scale(0.8);
    opacity: 0.5;
  }
  40% {
    transform: scale(1.2);
    opacity: 1;
  }
}

.typing-indicator span {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #999;
  animation: typing 1.4s infinite ease-in-out;
}

.typing-indicator span:nth-child(1) {
  animation-delay: -0.32s;
}

.typing-indicator span:nth-child(2) {
  animation-delay: -0.16s;
}

@keyframes typing {
  0%,
  80%,
  100% {
    transform: scale(0.8);
    opacity: 0.5;
  }
  40% {
    transform: scale(1);
    opacity: 1;
  }
}

/* 输入区域 */
.input-container {
  padding: 20px;
  border-top: 1px solid rgba(0, 0, 0, 0.1);
  background: rgba(255, 255, 255, 0.8);
  backdrop-filter: blur(10px);
}

/* 知识库开关（紧凑版） */
.knowledge-base-switch-compact {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  margin-bottom: 0;
}

.knowledge-base-switch-compact .switch-label {
  font-size: 13px;
  color: #666;
  font-weight: 500;
  white-space: nowrap;
  user-select: none;
}

.input-wrapper {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  max-width: 800px;
  margin: 0 auto;
  background: white;
  border-radius: 25px;
  padding: 8px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  border: 1px solid rgba(0, 0, 0, 0.05);
}

.file-upload {
  flex-shrink: 0;
  position: relative;
  display: inline-block;
  cursor: pointer;
  padding: 12px;
  border-radius: 50%;
  transition: all 0.2s ease;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
}

.file-upload.disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background: #d0d0d0;
}

.file-upload:not(.disabled):hover {
  transform: scale(1.05);
  box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
  background: linear-gradient(135deg, #5568d3 0%, #6a3d8f 100%);
}

.file-upload:not(.disabled):hover .upload-icon {
  color: #ffffff;
  filter: brightness(1.2);
}

.file-upload input[type="file"] {
  position: absolute;
  left: -9999px;
  opacity: 0;
  pointer-events: none;
}

.upload-icon {
  font-size: 20px;
  color: white;
}

/* 输入框包装器 */
.input-field-wrapper {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: 4px;
}

.input-field {
  flex: 1;
  display: flex;
  align-items: center;
}

.message-input {
  width: 100%;
  min-height: 44px;
  max-height: 120px;
  resize: none;
  border: none;
  border-radius: 20px;
  padding: 12px 16px;
  font-size: 15px;
  line-height: 1.4;
  outline: none;
  background: transparent;
  transition: all 0.2s ease;
}

.message-input:focus {
  background: rgba(0, 0, 0, 0.02);
}

.send-btn {
  padding: 12px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  font-size: 14px;
  transition: all 0.2s ease;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}

.send-btn:hover:not(:disabled) {
  transform: scale(1.05);
  box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}

.send-btn:disabled {
  background: #ccc;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.send-btn svg {
  width: 20px;
  height: 20px;
}

.selected-files {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 8px;
  max-width: 720px;
  margin-left: auto;
  margin-right: auto;
}

.file-limit-tip {
  font-size: 11px;
  color: #8a8a8a;
  text-align: right;
  padding-right: 2px;
}

.file-list-container {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.file-preview {
  display: flex;
  align-items: center;
  background: #f7f7f9;
  padding: 8px 12px;
  border-radius: 12px;
  font-size: 12px;
  gap: 8px;
  border: 1px solid rgba(0, 0, 0, 0.04);
  box-shadow: none;
  transition: all 0.2s ease;
  animation: slideIn 0.3s ease-out;
}

.file-preview.uploading {
  opacity: 0.8;
  background: #eef2ff;
}

.file-status {
  color: #5674d6;
  font-size: 11px;
  margin-left: 6px;
  font-weight: 400;
}

.file-preview:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}

.file-info {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1;
}

.file-info svg {
  width: 16px;
  height: 16px;
  color: #7a7a7a;
  flex-shrink: 0;
}

.file-name {
  font-size: 12px;
  font-weight: 400;
  color: #444;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 180px;
}

.file-size {
  font-size: 11px;
  color: #a0a0a0;
  white-space: nowrap;
}

.remove-file-btn {
  background: rgba(0, 0, 0, 0.06);
  border: none;
  color: #666;
  cursor: pointer;
  font-size: 12px;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  transition: all 0.2s ease;
  box-shadow: none;
}

.remove-file-btn:hover {
  background: rgba(0, 0, 0, 0.12);
  color: #333;
}

.remove-file-btn svg {
  width: 12px;
  height: 12px;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .chat-container {
    flex-direction: column;
  }

  .sidebar {
    width: 100%;
    height: auto;
    max-height: 200px;
    margin: 10px;
    border-radius: 20px;
  }

  .sidebar-collapsed {
    width: 100%;
    height: 60px;
  }

  .sidebar-collapsed .conversation-list {
    display: none;
  }

  .main-content {
    margin: 10px;
    border-radius: 20px;
  }

  .messages-container {
    padding: 15px;
  }

  .input-container {
    padding: 15px;
  }

  .input-wrapper {
    padding: 6px;
  }

  .message-bubble {
    padding: 12px 16px;
  }

  .page-title-bar {
    padding: 16px 15px 12px;
  }

  .logo-icon {
    width: 50px;
    height: 50px;
  }

  .main-title {
    font-size: 20px;
  }

  .slogan-text {
    font-size: 28px;
  }
}

@media (max-width: 480px) {
  .sidebar-header {
    padding: 15px;
  }

  .chat-header {
    padding: 15px;
  }

  .messages-container {
    padding: 10px;
  }

  .input-container {
    padding: 10px;
  }

  .message-bubble {
    padding: 10px 14px;
    font-size: 14px;
  }

  .file-preview {
    padding: 8px 12px;
    font-size: 12px;
  }

  .file-name {
    max-width: 150px;
  }

  .clear-btn {
    padding: 8px 12px;
    font-size: 12px;
  }

  .clear-btn .btn-text {
    display: none;
  }

  .clear-btn .el-icon {
    font-size: 18px;
    width: 18px;
    height: 18px;
  }

  .page-title-bar {
    padding: 12px 10px 10px;
  }

  .logo-icon {
    width: 45px;
    height: 45px;
  }

  .main-title {
    font-size: 18px;
  }

  .slogan-text {
    font-size: 24px;
    letter-spacing: 2px;
  }
}
</style>
